package geradordao;

import java.util.ArrayList;
import java.util.List;


/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
/**
 *
 * @author Iana
 */
public class AnalisadorSintatico {

    private List<Token> tokens;
    private List<String> erros = new ArrayList<String>();
    private Token token;
    private Integer pToken = 0;
    private Token escopo;
    private String classe;
    private String chaveE;
    private String id;

    private void nextToken() {
        if (tokens.size() > pToken) {
            token = tokens.get(pToken);
            pToken++;
        }
    }

    public AnalisadorSintatico(List<Token> tokens) {
        this.tokens = tokens;
    }

    public void analisar() {
        pToken = 0;
        nextToken();
        QueryList();
        if ((token.getImagem().equals("$")) || (erros.isEmpty())) {
            System.out.println("Analisador Sintatico reconheceu!");
        }
    }

    //<Query_List> ::= <Query> ';' <Query_List> |
    void QueryList() {
        if (token.getImagem().equalsIgnoreCase("create") || token.getImagem().equalsIgnoreCase("alter")) {
            Query();
            if (token.getImagem().equals(";")) {
                nextToken();
                QueryList();
            } else {
                setaErro("QueryList", ";", token.getImagem(), token.getLinha());
            }
        }
    }

    // <Query> ::= <Alter Stm> | <Create Stm>
    void Query() {
        if (token.getImagem().equalsIgnoreCase("create")) {
            CreateStm();
        } else if (token.getImagem().equalsIgnoreCase("alter")) {
            AlterStm();
        } else {
            setaErro("Query", "alter ou create", token.getImagem(), token.getLinha());
        }
    }

    // <Alter Stm> ::= alter table Id add <Constraint>
    void AlterStm() {
        if (token.getImagem().equalsIgnoreCase("alter")) {
            nextToken();
            if (token.getImagem().equalsIgnoreCase("table")) {
                nextToken();
                if (token.getClasse().equalsIgnoreCase("ID")) {
                    this.escopo = token;
                    TabelaSimbolos.setEscopoSimbolo(token, escopo);
                    nextToken();
                    if (token.getImagem().equalsIgnoreCase("add")) {
                        nextToken();
                        Constraint();
                    } else {
                        setaErro("AlterStm", "add", token.getImagem(), token.getLinha());
                    }
                } else {
                    setaErro("AlterStm", "ID", token.getClasse(), token.getLinha());
                }
            } else {
                setaErro("AlterStm", "table", token.getImagem(), token.getLinha());
            }
        } else {
            setaErro("AlterStm", "alter", token.getImagem(), token.getLinha());
        }
    }

    //<CreateStm>  ::= create table Id '(' <Field Def List> ')' ';'
    void CreateStm() {
        if (token.getImagem().equalsIgnoreCase("create")) {
            nextToken();
            if (token.getImagem().equalsIgnoreCase("table")) {
                nextToken();
                if (token.getClasse().equalsIgnoreCase("ID")) {
                    this.escopo = token;
                    classe = token.getImagem();
//                    Simbolo simbolo = new Simbolo();
//                    simbolo.setClassificacao("table");
//                    simbolo.setTipo(token.getClasse());
//                    simbolo.setEscopo(escopo);
//                    simbolo.setToken(token);
//                    TabelaSimbolos.addSimbolo(simbolo);
                    TabelaSimbolos.addSimbolo(token, escopo.getImagem(), "table");
                    TabelaSimbolos.setEscopoSimbolo(token, escopo);
                    nextToken();
                    if (token.getImagem().equals("(")) {
                        nextToken();
                        FieldDefList();
                        if (token.getImagem().equals(")")) {
                            nextToken();
                        } else {
                            setaErro("CreateStm", ")", token.getImagem(), token.getLinha());
                        }
                    } else {
                        setaErro("CreateStm", "(", token.getImagem(), token.getLinha());
                    }
                } else {
                    setaErro("CreateStm", "ID", token.getClasse(), token.getLinha());
                }
            } else {
                setaErro("CreateStm", "table", token.getImagem(), token.getLinha());
            }
        } else {
            setaErro("CreateStm", "create", token.getImagem(), token.getLinha());
        }
    }

    //<FieldDefList> ::= <FieldDef> <FieldDefList2>
    void FieldDefList() {
        FieldDef();
        FieldDefList2();
    }

    //<FieldDefList2> ::= ',' <Field Def> <Field Def List 2> | <ConstraintList>
    void FieldDefList2() {
        if (token.getImagem().equals(",")) {
            Token lookAhead = lookAhead();
            if (lookAhead.getClasse().equalsIgnoreCase("ID")) {
                TabelaSimbolos.setEscopoSimbolo(token, escopo);
                nextToken();
                FieldDef();
                FieldDefList2();
            } else {
                ConstraintList();
            }
        }
    }

    //<FieldDef>   ::= Id <Type> <Not Null>
    void FieldDef() {
        if (token.getClasse().equalsIgnoreCase("ID")) {
            TabelaSimbolos.addSimbolo(token, escopo.getImagem(), "atributo");
            TabelaSimbolos.setEscopoSimbolo(token, escopo);
            nextToken();
            Type();
            NotNull();
        }
    }

    //<Not Null> ::= not null |
    void NotNull() {
        if (token.getImagem().equalsIgnoreCase("not")) {
            nextToken();
            if (token.getImagem().equalsIgnoreCase("null")) {
                nextToken();
            } else {
                setaErro("NotNull", "null", token.getImagem(), token.getLinha());
            }
        }
    }

    //<Type>  ::=  bit  | date  |  time | timestamp |  decimal  |  real |  float
    //|  smallint |  integer | int  |  interval |  character |  varchar <Tamanho>
    void Type() {
        if (token.getClasse().equals("PR")) {
            if ((token.getImagem().equalsIgnoreCase("varchar")) || token.getImagem().equalsIgnoreCase("varchar2")) {
                nextToken();
                Tamanho();
            } else {
                nextToken();
            }
        } else {
            setaErro("Tipo", "PR", token.getClasse(), token.getLinha());
        }
    }

    //<Tamanho> ::= '(' IntegerLiteral ')'
    void Tamanho() {
        if (token.getImagem().equals("(")) {
            nextToken();
            if (token.getClasse().equalsIgnoreCase("CLI")) {
                nextToken();
                if (token.getImagem().equals(")")) {
                    nextToken();
                } else {
                    setaErro("Tamanho", ")", token.getImagem(), token.getLinha());
                }
            } else {
                setaErro("Tamanho", "CLI", token.getClasse(), token.getLinha());
            }
        } else {
            setaErro("Tamanho", "(", token.getImagem(), token.getLinha());
        }
    }

    //<ConstraintList> ::= ',' <ConstraintType> <ConstraintList> |
    void ConstraintList() {
        if (token.getImagem().equalsIgnoreCase(",")) {
            nextToken();
            ConstraintType();
            ConstraintList();
        }
    }

    //<Constraint> ::= constraint Id <Constraint Type>  | <Constraint Type>
    void Constraint() {
        if (token.getImagem().equalsIgnoreCase("constraint")) {
            nextToken();
            if (token.getClasse().equalsIgnoreCase("ID")) {
                TabelaSimbolos.setEscopoSimbolo(token, escopo);
                nextToken();
                ConstraintType();
            }
        } else {
            ConstraintType();
        }
    }

    //<Constraint Type> ::= primary key '(' <Id List> ')'
    //| foreign key '(' <Id List> ')' references Id '(' <Id List> ')'
    void ConstraintType() {
        if (token.getImagem().equalsIgnoreCase("primary")) {
            nextToken();
            if (token.getImagem().equalsIgnoreCase("key")) {
                nextToken();
                if (token.getImagem().equals("(")) {
                    nextToken();
                    TabelaSimbolos.addSimbolo(token, escopo.getImagem(), "primary");
                    TabelaSimbolos.setEscopoSimbolo(token, escopo);
                    IdList();
                    if (token.getImagem().equals(")")) {
                        nextToken();
                    } else {
                        setaErro("ConstraintType", ")", token.getImagem(), token.getLinha());
                    }
                } else {
                    setaErro("ConstraintType", "(", token.getImagem(), token.getLinha());
                }
            } else {
                setaErro("ConstraintType", "key", token.getImagem(), token.getLinha());
            }
        } else { //| foreign key '(' <Id List> ')' references Id '(' <Id List> ')'
            if (token.getImagem().equalsIgnoreCase("foreign")) {
                nextToken();
                if (token.getImagem().equalsIgnoreCase("key")) {
                    nextToken();
                    if (token.getImagem().equals("(")) {
                        nextToken();
                        id = token.getImagem();
                        TabelaSimbolos.addSimbolo(token, escopo.getImagem(), "foreing");
                        TabelaSimbolos.setEscopoSimbolo(token, escopo);
                        IdList();
                        if (token.getImagem().equals(")")) {
                            nextToken();
                            if (token.getImagem().equalsIgnoreCase("references")) {
                                nextToken();
                                if (token.getClasse().equalsIgnoreCase("ID")) {
                                    chaveE = token.getImagem();
                                    TabelaSimbolos.setReferenciaSimbolo(id, chaveE, escopo);
                                    nextToken();
                                    if (token.getImagem().equals("(")) {
                                        nextToken();
                                        IdList();
                                        if (token.getImagem().equals(")")) {
                                            nextToken();
                                        } else {
                                            setaErro("ConstraintType", ")", token.getImagem(), token.getLinha());
                                        }
                                    } else {
                                        setaErro("ConstraintType", ")", token.getImagem(), token.getLinha());
                                    }
                                } else {
                                    setaErro("ConstraintType", "ID", token.getImagem(), token.getLinha());
                                }
                            } else {
                                setaErro("ConstraintType", "references", token.getImagem(), token.getLinha());
                            }
                        } else {
                            setaErro("ConstraintType", ")", token.getImagem(), token.getLinha());
                        }
                    } else {
                        setaErro("ConstraintType", ")", token.getImagem(), token.getLinha());
                    }
                }
            }
        }
    }

//<Id List> ::= Id <Id List 2>
    void IdList() {
        if (token.getClasse().equalsIgnoreCase("ID")) {
            nextToken();
            IdList2();
        } else {
            setaErro("IdList", "PR", token.getClasse(), token.getLinha());
        }
    }

    //<Id List 2> ::= ',' Id <Id List 2> |
    void IdList2() {
        if (token.getClasse().equals(",")) {
            nextToken();
            if (token.getClasse().equalsIgnoreCase("ID")) {
                TabelaSimbolos.setEscopoSimbolo(token, escopo);
                nextToken();
                IdList2();
            }
        }
    }

    void setaErro(String metodo, String esperado, String veio, Integer linha) {
        erros.add("Falha: No metodo " + metodo + " espera-se " + esperado + " na linha " + linha + " veio " + veio);
    }

    public void printErros() {
        for (String erro : erros) {
            System.out.println(erro);
        }
    }

    public boolean temErros() {
        return erros.size() > 0;
    }

    public void mostraErros() {
        for (String erro : erros) {
            System.out.println(erro);
        }
    }

    private Token lookAhead() {
        return tokens.get(pToken);
    }
}
